import { Empty, Input, Tree } from 'antd'
import { isEmpty } from 'lodash-es'
import { useEffect, useState } from 'react'

import { getItemFromTree } from '@/utils/dataHelper'
import { requestWithTry } from '@/utils/request'

import AekSpin from '../AekSpin'

/**
 * 找到树中第一个可选节点
 * @param {Object} param0
 * @param {[]} param0.treeData 树数据
 * @param {import("antd/es/tree").TreeProps['fieldNames']} param0.fieldNames
 * @returns
 */
const findFirstChild = ({ treeData, fieldNames }) => {
  const { children: fieldNamesChildren = 'children' } = fieldNames
  for (let i = 0; i < treeData.length; i++) {
    const item = treeData[i]
    if (
      isEmpty(item[fieldNamesChildren]) &&
      !item.disabled &&
      (item.selectable === undefined || item.selectable) &&
      !item.disableCheckbox
    ) {
      return item
    }
    if (item[fieldNamesChildren]) {
      const child = findFirstChild({ treeData: item[fieldNamesChildren], fieldNames })
      if (child) return child
    }
  }
  return null
}

/**
 * 过滤搜索的数据
 * @param {Object} param0
 * @param {Array<Object>} param0.treeData 树数据
 * @param {string} param0.searchValue 搜索字符串
 * @param {import("antd/es/tree").TreeProps['fieldNames']} param0.fieldNames
 * @returns
 */
const filterCheckedNodes = ({ treeData, searchValue, fieldNames }) => {
  const { title: fieldNamesTitle = 'name', children: fieldNamesChildren = 'children' } = fieldNames
  // 递归函数，用于过滤节点
  function filter(node) {
    if (!node) return null
    // 如果节点没有children属性，则直接判断是否保留当前节点
    if (!node[fieldNamesChildren]) {
      return node[fieldNamesTitle].includes(searchValue) ? node : null
    }
    // 递归过滤子节点
    const filteredChildren = node[fieldNamesChildren].map(filter).filter(child => child !== null)
    // 如果子节点中包含搜索关键字，则保留当前节点
    if (filteredChildren.length > 0 || node[fieldNamesTitle].includes(searchValue)) {
      return {
        ...node,
        [fieldNamesChildren]: filteredChildren.length > 0 ? filteredChildren : undefined,
      }
    }
    return null
  }
  return treeData.map(filter).filter(node => node !== null)
}

/**
 * 高亮搜索的字符串
 * @param {Object} param0
 * @param {Array<Object>} param0.treeData 树数据
 * @param {string} param0.searchValue 搜索字符串
 * @param {import("antd/es/tree").TreeProps['fieldNames']} param0.fieldNames
 * @returns
 */
const highlightCheckedNodes = ({ treeData, searchValue, fieldNames }) => {
  const {
    key: fieldNamesKey = 'id',
    title: fieldNamesTitle = 'name',
    children: fieldNamesChildren = 'children',
  } = fieldNames
  const loop = data =>
    data.map(item => {
      const strTitle = item[fieldNamesTitle]
      const index = strTitle.indexOf(searchValue)
      const beforeStr = strTitle.substring(0, index)
      const afterStr = strTitle.slice(index + searchValue.length)
      const newTitle =
        index > -1 ? (
          <span key={item[fieldNamesKey]}>
            {beforeStr}
            <span className='c-#f50'>{searchValue}</span>
            {afterStr}
          </span>
        ) : (
          <span key={item[fieldNamesKey]}>{strTitle}</span>
        )
      if (item[fieldNamesChildren]) {
        return {
          ...item,
          [fieldNamesTitle]: newTitle,
          [fieldNamesKey]: item[fieldNamesKey],
          [fieldNamesChildren]: loop(item[fieldNamesChildren]),
        }
      }
      return {
        ...item,
        [fieldNamesTitle]: newTitle,
        [fieldNamesKey]: item[fieldNamesKey],
      }
    })
  return loop(treeData)
}

/**
 * @typedef {Object} OtherProps
 * @property {Boolean} [showSearch] 是否显示搜索框
 * @property {String} [searchType] 搜索方式，filter：过滤，highlight：高亮
 * @property {Promise} [api] 接口
 * @property {Object} [params] 额外参数
 * @property {funciton} [contentRender] 处理返回值
 * @property {boolean} [canCancelSel] 是否可取消选中，只可在单选的时候使用
 * @property {boolean} [defaultSelectFirst] 是否默认选择第一个，需要满足以下条件【!disabled && selectable && !disableCheckbox】
 * @property {String} [reloadKey] 当该值变化时，重新加载数据
 */

/**
 * @typedef {import("antd/es/tree").TreeProps & OtherProps} AekTreeProps
 */

/**
 * 树
 * @param {AekTreeProps} props
 * @returns
 */
const AekTree = props => {
  const {
    showSearch = true,
    searchType = 'highlight',
    api,
    params = {},
    contentRender = _ => _,
    canCancelSel = true,
    defaultSelectFirst = false,
    reloadKey,
    fieldNames = {},
    onSelect,
    treeData: dataSource,
    ...otherProps
  } = props
  const {
    key: fieldNamesKey = 'id',
    title: fieldNamesTitle = 'name',
    children: fieldNamesChildren = 'children',
  } = fieldNames

  const [loading, setLoading] = useState(false)
  const [originTreeData, setOriginTreeData] = useState([])
  const [treeData, setTreeData] = useState([])
  const [selectedKeys, setSelectedKeys] = useState([])

  const loadOrgData = async () => {
    setLoading(true)
    const { data } = await requestWithTry(api, params).finally(() => {
      setLoading(false)
    })
    const tree = contentRender(data.content || data || [])
    setOriginTreeData(tree)
    setTreeData(tree)
  }

  useEffect(() => {
    if (!api) return
    loadOrgData()
  }, [api, JSON.stringify(params), reloadKey])

  useEffect(() => {
    if (!dataSource) return
    setOriginTreeData(dataSource)
    setTreeData(dataSource)
  }, [dataSource])

  useEffect(() => {
    if (!defaultSelectFirst) return
    const firstChild = findFirstChild({ treeData: originTreeData, fieldNames })
    if (!firstChild) return
    // 当前已选中一个数据，判断新的数据源里是否包含该数据，如果包含，则不做任何操作
    if (selectedKeys[0]) {
      const selectedItem = getItemFromTree({
        dataSource: originTreeData,
        childrenField: fieldNamesChildren,
        fieldName: fieldNamesKey,
        fieldValue: selectedKeys[0],
      })
      if (selectedItem) return
    }
    // 选择第一个
    onSelect?.([firstChild[fieldNamesKey]], {
      selected: true,
      node: firstChild,
      selectedNodes: [firstChild],
    })
    setSelectedKeys([firstChild[fieldNamesKey]])
  }, [JSON.stringify(originTreeData)])

  const onSearch = input => {
    // 使用过滤函数
    if (searchType === 'filter') {
      const filteredTree = filterCheckedNodes({
        treeData: originTreeData,
        searchValue: input,
        fieldNames,
      })
      setTreeData(filteredTree)
    }
    // 使用高亮函数
    if (searchType === 'highlight') {
      const highlightTree = highlightCheckedNodes({
        treeData: originTreeData,
        searchValue: input,
        fieldNames,
      })
      setTreeData(highlightTree)
    }
  }

  return (
    <AekSpin spinning={loading}>
      <div className='flex h-full flex-col'>
        {showSearch && (
          <Input.Search className='mb10px' placeholder='请输入关键字搜索' allowClear onSearch={onSearch} />
        )}
        <div className='flex-grow-1 overflow-y-auto'>
          {isEmpty(treeData) ? (
            <Empty />
          ) : (
            <Tree
              {...{
                fieldNames: { key: fieldNamesKey, title: fieldNamesTitle, children: fieldNamesChildren },
                defaultExpandAll: true,
                treeData,
                selectedKeys,
                ...otherProps,
                onSelect: (selectedKeys, info) => {
                  // 不做取消选中的操作
                  if (!canCancelSel) {
                    if (!info.selected) return
                    setSelectedKeys([info.node[fieldNamesKey]])
                    onSelect?.([info.node[fieldNamesKey]], info)
                    return
                  }
                  setSelectedKeys(selectedKeys)
                  onSelect?.(selectedKeys, info)
                },
              }}
            />
          )}
        </div>
      </div>
    </AekSpin>
  )
}

export default AekTree
